//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Windows.Documents;
using OpenLS.Spreadsheet.Expressions;
using OpenLS.Spreadsheet.Extensibility;
using OpenLS.Spreadsheet.Internals;

namespace OpenLS.Spreadsheet
{
    public abstract class CellsRange : SimpleRange
    {
        internal override bool IsProtected
        {
            get
            {
                if (this.Worksheet.Protection == null)
                    return false;
                return Enumerable.Any(this.GetAllCells(), c => c.IsLocked);
            }
        }
        internal int MaxColumnIndex { get { return this.Column + ColumnSpan - 1; } }
        internal int MaxRowIndex { get { return this.Row + RowSpan - 1; } }
        internal override void SetStyleRecordNoUndo(CellFormattingRecord cellFormattingRecord)
        {
            foreach (Cell cell in GetAllCells())
                cell.SetFormattingRecordSafe(cellFormattingRecord);
        }
        internal override IEnumerable<SimpleRange> GetSimpleRanges()
        {
            yield return this;
        }

        internal override bool Contains(Range range)
        {
            if (range is CellsRange)
            {
                var r = (CellsRange) range;
                return Contains(r.TopLeftCell) && Contains(r.BottomRight);
            }
            return ContainsCompositeRange(range);
        }

        internal override void CopyFromImp(CellAddress sourceTopLeft, IEnumerable<SimpleRange> sources, PasteInfo pasteInfo)
        {
           // if (!(source is CellsRange))
             //   throw new NotSupportedException();
          //  var cellsRange = (CellsRange)source;
            CellRelativeAddress offset = Address.TopLeft - sourceTopLeft;//
            var targetTopLeft = Address.TopLeft;
            var fn = pasteInfo.Functions;
            using (Worksheet.Workbook.CreateUndoScope((StringTable.Copy))) //\\ loc
            {
                foreach (Cell cell in GetNonEmptyCells())
                    fn.ClearCell(cell);
                foreach (var source in sources)
                {
                    if (fn.CopyRowcolumnFunction != null)
                    {
                        foreach (Column c in source.GetNonEmptyColumns())
                            fn.CopyRowcolumnFunction(c, Worksheet.Columns[c.Index + offset.Column]);
                        foreach (Row row in source.GetNonEmptyRows())
                            fn.CopyRowcolumnFunction(row, Worksheet.Rows[row.Index + offset.Row]);
                    }
                    fn.CopyRange(source, this, pasteInfo);
                    if (source.IsSingleCell)
                    {
                        Cell s = source.TopLeftCell;
                        foreach (Cell cell in GetAllCells())
                            fn.CopyFunction(s, cell, pasteInfo);
                        return;
                    }
                    foreach (Cell cell in source.GetNonEmptyCells())
                    {
                        fn.CopyFunction(cell,
                                        Worksheet[pasteInfo.GetAddress(cell.Address, sourceTopLeft, targetTopLeft)],
                                        pasteInfo); //\\ cell.Address + offset]);
                    }
                }
            }
        }
        
        internal override FlowDocument ToFlowDocument(string columnSeparator)
        {
            var result = new FlowDocument();
            System.Windows.Documents.Table table = new System.Windows.Documents.Table();
            result.Blocks.Add(table);
            TableRowGroup rowGroup = new TableRowGroup();
            table.RowGroups.Add(rowGroup);
            for (int row = TopLeftCell.Row.Index; row <= BottomRight.Row.Index; row++)
            {
                TableRow tableRow = new TableRow();
                int r = row;
                Func<int, Cell> f = (cc) => Worksheet[r, cc];
                int maxColumn =
                    Enumerable.Max(
                        Enumerable.Select(
                            Enumerable.Where(
                                Enumerable.Select(Enumerable.Range(TopLeftCell.Column.Index, BottomRight.Column.Index),f), c => !c.IsVolatile), c => c.Column.Index),
                        TopLeftCell.Column.Index);
                for (int column = TopLeftCell.Column.Index; column <= maxColumn; column++)
                {
                    Cell c = Worksheet[row, column];
                    tableRow.Cells.Add(c.ToTableCell());
                }
                rowGroup.Rows.Add(tableRow);
              
            }
            return result;
        }
        //\\internal abstract RangeAddress Address { get;}
        public void ApplySharedFormula(string formula)
        {
            using (this.Worksheet.Workbook.CreateUndoScope(StringTable.FormatCells))
            {
                using ((this.Worksheet.Workbook.CreateCalculationDisabler()))
                {
                    TopLeftCell.Formula = formula;
                    var sharedExpression = new SharedExpression(Address, TopLeftCell.Expression);
                    Worksheet.Workbook.CalculationProperties.CalcId++;
                    foreach (Cell c in GetAllCells())
                    {
                        var sharedExpressionReference = new SharedExpressionReference(sharedExpression, c);
                        c.Expression = sharedExpressionReference;
                        c.InvalidateExpression();
                    }
                    foreach (Cell c in GetAllCells())
                    {
                        c.Recalculate();
                    }
                }
            }
        }

        internal static CellsRange Create(Worksheet worksheet, RangeAddress rangeAddress)
        {
            return Create(worksheet, rangeAddress, true);
        }
       //\\ public abstract Cell TopLeft { get;}
        public abstract Cell BottomLeft { get;}
        public abstract Cell TopRight { get;}
        public abstract Cell BottomRight { get;}
        internal abstract IEnumerable<Cell> GetLeftCells();
        internal abstract IEnumerable<Cell> GetRightCells();
        internal abstract IEnumerable<Cell> GetTopCells();
        internal abstract IEnumerable<Cell> GetBottomCells();


        internal static CellsRange Create(Worksheet worksheet, RangeAddress rangeAddress, bool adjust)
        {
            if (rangeAddress.ColumnSpan == 1 && rangeAddress.RowSpan == 1)
                return new SingleCellRange(worksheet, rangeAddress.TopLeft);
            return new MultiCellsRange(worksheet, rangeAddress,RangeType.Dynamic,  adjust);
        }

        internal  FullColumnRange ToFullColumnRange()
        {
            return new FullColumnRange(TopLeftCell.Column, BottomRight.Column);
        }

        internal bool Intersects(CellsRange cellsRange)
        {
            if (Contains(cellsRange.TopLeftCell))
                return true;
            if (Contains(cellsRange.TopRight))
                return true;
            if (Contains(cellsRange.BottomLeft))
                return true;
            if (Contains(cellsRange.BottomRight))
                return true;
            if (cellsRange.Contains(TopLeftCell))
                return true;
            if (cellsRange.Contains(TopRight))
                return true;
            if (cellsRange.Contains(BottomLeft))
                return true;
            if (cellsRange.Contains(BottomRight))
                return true;
            return false;
        }
        public override sealed bool IsFullColumn
        {
            get { return false; }
        }
        public override sealed bool IsFullRow
        {
            get { return false; }
        }


        internal  FullRowRange ToFullRowRange()
        {
            return new FullRowRange(TopLeftCell.Row, BottomRight.Row);
        }

        internal override bool IsSingleCell { get { return RowSpan * ColumnSpan == 1; } }

    }
}
